//
// Created by lxinp on 2023/5/25.
//

#ifndef DATASTRUCTURE_GRAPH_H
#define DATASTRUCTURE_GRAPH_H

#include <string>
#include <vector>
#include <unordered_map>
#include <unordered_set>

using namespace std;


class Graph {
private:
    using Vertex = unsigned int;

    struct Edge {
        Vertex src;
        Vertex dst;
        int weight;
        Edge(Vertex _src, Vertex _dst, int _weight) : src(_src), dst(_dst), weight(_weight) {}
    };

    enum Label {
        Unsearched = 0,
        Visiting,
        Searched
    };

public:
    Graph() = default;  // creates an empty graph

    /**
     * Creates a graph given the provided information
     * @param graphInfo Each pair of graphInfo maps a vertex to a vector of vertices it connects to;
     *                  Note that the connectivity is directed
     */
    explicit Graph(vector<pair<Vertex, vector<Vertex>>>& graphInfo);

    vector<Vertex> GetAdjacentVertices(Vertex u) const;
    vector<Vertex> GetAllVertices() const;
    Edge GetEdge(Vertex u, Vertex v) const;
    vector<Edge> GetAllEdges() const;

    void InsertVertex(Vertex u);
    void RemoveVertex(Vertex u);
    void InsertEdge(Vertex u, Vertex v);
    void RemoveEdge(Vertex u, Vertex v);

    void SetEdgeWeight(Vertex u, Vertex v, int weight);
    int GetEdgeWeight(Vertex u, Vertex v) const;
    void SetVertLabel(Vertex u, Label label);
    Label GetVertLabel(Vertex u) const;
    void SetEdgeLabel(Vertex u, Vertex v, Label label);
    Label GetEdgeLabel(Vertex u, Vertex v) const;

private:
    unordered_map<Vertex, unordered_set<Edge*>> underlying_graph;
    unordered_map<Vertex, Label> vertex_labels;
    unordered_set<Edge*> edges;

private:
    Vertex nextVertex();
    bool ifVertExists(Vertex u) const;
    void ifConnected(Vertex u, Vertex v) const;
};

#endif //DATASTRUCTURE_GRAPH_H